//
// Created by hunan on 24-8-22.
//

#include "protocol_connection.h"

#include "./../protocol_center/protocol_center.h"
#include "./../protocol_center/protocol_parser.h"
#include "./../socket_module/socket_address.h"
#include "./../socket_module/socket_connection.h"
#include "./../socket_module/socket_connection_util.h"
#include "./../socket_module/tcp_socket_connection.h"
#include "./../log_module/log_public_module.h"
#include "protocol_operation.h"
#include "protocol_errno.h"

#include <memory>

static int GetConnectionType(int protocol_type, int use_tls)
{
    int connection_type = 0;
    switch (protocol_type)
    {
        case ProtocolCenter::ProtocolType::PROTOCOL_TCP:
        {
            connection_type = use_tls ? SocketConnection::ConnectionType::EmConnectionTypeTcpTls :
                                        SocketConnection::ConnectionType::EmConnectionTypeTcp;
            break;
        }
        case ProtocolCenter::ProtocolType::PROTOCOL_UDP:
        {
            connection_type = use_tls ? SocketConnection::ConnectionType::EmConnectionTypeUdpTls :
                                        SocketConnection::ConnectionType::EmConnectionTypeUdp;
            break;
        }
        default:
            break;
    }
    return connection_type;
}


ProtocolConnection::ProtocolConnectionObserver::ProtocolConnectionObserver(ProtocolConnection * connection)
{
    m_protocol_connection = connection;
}

void ProtocolConnection::ProtocolConnectionObserver::Update(SocketCommand * command)
{
    if (m_protocol_connection)
    {
        m_protocol_connection->OnEvent(command);
    }
}

ProtocolConnection::ProtocolConnection(Loop * loop, ProtocolOperation * operation) : m_protocol_data()
{
    m_loop               = loop;
    m_protocol_operation = operation;
    m_need_reconnect     = false;
}

ProtocolConnection::~ProtocolConnection()
{
    m_socket_connection.reset();
}

void ProtocolConnection::OnProtocolConnect(ProtocolConnection::OnConnect && connect_fn)
{
    m_connection_cb = connect_fn;
}

void ProtocolConnection::OnProtocolMessage(OnMessage && message_fn)
{
    m_message_cb = message_fn;
}

void ProtocolConnection::OnProtocolConnErr(ProtocolConnection::OnConnErr && conn_err)
{
    m_connect_error_cb = conn_err;
}

void ProtocolConnection::OnProtocolDisConn(ProtocolConnection::OnDisConn && disconn)
{
    m_disconnection_cb = disconn;
}

ProtocolOperation * ProtocolConnection::GetProtocolOperation() const
{
    return m_protocol_operation;
}

ProtocolParser::ProtocolData & ProtocolConnection::ProtocolData()
{
    return m_protocol_data;
}

int ProtocolConnection::InitServerUrl(const std::string & url)
{
    return InitServerUrl(url, {});
}

int ProtocolConnection::InitServerUrl(const std::string & url, const ProtocolConnection::Option & option)
{
    auto p = ProtocolParser::ParserUrl(url);
    if (p.m_host.empty() || p.m_port == 0)
    {
        return EmProtocolErrno_ERROR;
    }
    m_url           = url;
    m_options       = option;
    m_protocol_data = p;
    return InitServerUrl();
}

void ProtocolConnection::ProcessConnected(SocketCommand * command)
{
    if (m_connection_cb)
    {
        m_connection_cb(this, EmProtocolErrno::EmProtocolErrno_SUCCESS);
    }
}

void ProtocolConnection::ProcessError(SocketCommand * command)
{
    if (m_connect_error_cb)
    {
        m_connect_error_cb(this, EmProtocolErrno::EmProtocolErrno_ERROR);
    }
    ProcessDisconnect(command);
}

void ProtocolConnection::ProcessDisconnect(SocketCommand * command)
{
    LOG_PROTO_CONN_MODULE("ProtocolConnection(%p) ProcessDisconnect socket command:%d", this, command->Cmd());
    m_need_reconnect = true;
    CloseConnection();
    if (m_disconnection_cb)
    {
        m_disconnection_cb(this, EmProtocolErrno::EmProtocolErrno_DISCONNECT);
    }
}

void ProtocolConnection::ProcessRead(SocketCommand * command)
{
    if (m_protocol_operation)
    {
        int use_data_size = 0;
        int parse_result  = m_protocol_operation->ParserProtocol(command->Data(), command->DataLength(), use_data_size);
        if (parse_result == ProtocolOperation::FINISH)
        {
            if (m_message_cb)
            {
                m_message_cb(this, m_protocol_operation);
            }
        }
        else if (parse_result == ProtocolOperation::BAD)
        {
            use_data_size = command->DataLength();
            // todo 协议错误-断开连接
        }
        else if (parse_result == ProtocolOperation::NEED_DATA)
        {

        }
        if (use_data_size > 0)
        {
            command->SetUseDataLength(use_data_size);
        }
    }
}

void ProtocolConnection::OnEvent(SocketCommand * command)
{
    if (!command)
    {
        return;
    }
    switch (command->Cmd())
    {
        case SocketCommand::EmCmdConnect:
        {
            ProcessConnected(command);
            break;
        }
        case SocketCommand::EmCmdError:
        {
            ProcessError(command);
            break;
        }
        case SocketCommand::EmCmdDisconnect:
        {
            ProcessDisconnect(command);
            break;
        }
        case SocketCommand::EmCmdRead:
        {
            ProcessRead(command);
            break;
        }
        default:
            break;
    }
}

int ProtocolConnection::InitServerUrl()
{
    if (!m_socket_connection || m_socket_connection->ConnectState() == SocketConnection::EmConnectStateDisconnected)
    {
        int result = EmProtocolErrno_SUCCESS;
        if (m_protocol_data.m_protocol.m_protocol_type != ProtocolCenter::ProtocolType::PROTOCOL_INIT)
        {
            auto type = GetConnectionType(m_protocol_data.m_protocol.m_protocol_type, m_protocol_data.m_protocol.m_use_tls_or_dtls);
            if (type != SocketConnection::ConnectionType::EmConnectionTypeInit)
            {
                m_socket_connection = SocketConnectionUtil::CreateSocketConnection(type, m_loop);
            }
            else
            {
                result = EmProtocolErrno_ERROR;
            }
        }
        else
        {
            result = EmProtocolErrno_ERROR;
        }
        if (result != EmProtocolErrno_SUCCESS)
        {
            return result;
        }
        std::vector<SocketAddress> addr_vec{};
        SocketAddress::Address(m_protocol_data.m_host, m_protocol_data.m_port, addr_vec);
        if (!addr_vec.empty())
        {
            auto address = *addr_vec.data();
            m_socket_connection->RegisterSocketObserver(std::make_shared<ProtocolConnection::ProtocolConnectionObserver>(this));
            m_socket_connection->OpenSocket(SocketAddress(), SocketConnection::Option{});
            m_socket_connection->AsyncConnect(
                address,
                SocketConnection::Option{
                    .m_host_name = m_protocol_data.m_host,
                    .m_enable_nonblock = 1,
                    .m_ssl_key_log_file = m_options.m_ssl_key_log_file});

            return EmProtocolErrno_SUCCESS;
        }
        return EmProtocolErrno_ERROR;
    }
    else
    {
        return EmProtocolErrno_CONNECTED;
    }
}

void ProtocolConnection::CloseConnection()
{
    if (m_socket_connection)
    {
        m_socket_connection->Close();
        m_socket_connection = nullptr;
    }
    if (m_need_reconnect)
    {
        m_need_reconnect = false;
        InitServerUrl();
    }
}

int ProtocolConnection::SendMessage(const uint8_t * data, int data_length) const
{
    if (m_socket_connection)
    {
        return m_socket_connection->AsyncWrite(data, data_length);
    }
    return -1;
}
